package com.sp.common.exception;

import cn.hutool.core.exceptions.ValidateException;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.sp.common.vo.Result;
import io.micrometer.common.util.StringUtils;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.MyBatisSystemException;
import org.springframework.context.support.DefaultMessageSourceResolvable;
import org.springframework.http.HttpStatus;
import org.springframework.validation.BindException;
import org.springframework.web.HttpMediaTypeNotSupportedException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.util.ContentCachingRequestWrapper;

import java.nio.file.AccessDeniedException;
import java.sql.SQLException;
import java.util.stream.Collectors;

/**
 * 
    * @ClassName: DefaultExceptionAdvice
    * @Description: 异常通用处理
    * @author bin.ants
    * @date 2019年6月25日
    *
 */
@Slf4j
@ControllerAdvice
@ResponseBody
public class DefaultExceptionAdvice {
    /**
     * IllegalArgumentException异常处理返回json
     * 返回状态码:400
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({IllegalArgumentException.class})
    public Result badRequestException(IllegalArgumentException e) {
        return defHandler(e.getMessage(), e, false);
    }

    /**
     * UnauthorizedUserException异常处理返回json
     * 返回状态码:401
     */
    /*@ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler({UnauthorizedUserException.class})
    public Result unAuthUserRequestException(IllegalArgumentException e) {
        return defHandler("用户无访问权限", e);
    }*/
    /**
     * UnauthorizedClientException异常处理返回json
     * 返回状态码:401
     */
    /*@ResponseStatus(HttpStatus.UNAUTHORIZED)
    @ExceptionHandler({UnauthorizedClientException.class})
    public Result unAuthClientRequestException(IllegalArgumentException e) {
        return defHandler("客户端无访问权限", e);
    }
*/
    /**
     * AccessDeniedException异常处理返回json
     * 返回状态码:403
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({AccessDeniedException.class})
    public Result badMethodExpressException(AccessDeniedException e) {
        return defHandler("没有权限请求当前方法", e, false);
    }

    /**
     * 返回状态码:405
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({HttpRequestMethodNotSupportedException.class})
    public Result handleHttpRequestMethodNotSupportedException(HttpRequestMethodNotSupportedException e) {
        return defHandler("不支持当前请求方法", e, false);
    }

    /**
     * 返回状态码:415
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({HttpMediaTypeNotSupportedException.class})
    public Result handleHttpMediaTypeNotSupportedException(HttpMediaTypeNotSupportedException e) {
        return defHandler("不支持当前媒体类型", e, false);
    }

    /**
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({SQLException.class})
    public Result handleSQLException(SQLException e) {
        return defHandler("服务运行SQLException异常", e, true);
    }

    /**
     * BusinessException 业务异常处理
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BusinessException.class)
    public Result handleException(BusinessException e) {
        String msg;
        if (StringUtils.isNotEmpty(e.getMessage())) {
            msg = e.getMessage();
        } else {
            msg = "业务异常";
        }
        return defHandler(msg, e, false);
    }

    /**
     * ServiceException 业务异常处理，不打印错误日志
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ServiceException.class)
    public Result handleException(ServiceException e) {
        String msg;
        if (StringUtils.isNotEmpty(e.getMessage())) {
            msg = e.getMessage();
        } else {
            msg = "业务异常";
        }
        log.info(msg, e);
        return Result.failed(msg);
    }

    /**
     * 幂等异常
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(IdempotencyException.class)
    public Result handleException(IdempotencyException e) {
        return Result.failed(e.getMessage());
    }

    /**
     * 所有异常统一处理
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(Exception.class)
    public Result handleException(Exception e) {
        String msg = getErrMsg(e);
        log.warn(msg, e);
        return Result.failed(msg);
    }

    /**
     * 校验异常统一处理，不打印错误日志
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(ValidateException.class)
    public Result handleException(ValidateException e) {
        String msg;
        if (StringUtils.isNotEmpty(e.getMessage())) {
            msg = e.getMessage();
        } else {
            msg = "入参校验异常";
        }
        log.info(msg, e);
        return Result.failed(msg);
    }
    
    
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(BindException.class)
    public Result handleException(BindException e) {
    	String message = e.getBindingResult().getAllErrors().stream().map(DefaultMessageSourceResolvable::getDefaultMessage).collect(Collectors.joining(","));
    	return defHandler(message, e, false);
    }


    /**
     * 校验异常统一处理
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result handleException(MethodArgumentNotValidException e) {

        return defHandler( e.getBindingResult().getFieldError().getDefaultMessage(), e, false);
    }

    /**
     * 校验异常统一处理
     * 返回状态码:500
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MyBatisSystemException.class)
    public Result handleException(MyBatisSystemException e) {
        String message = e.getMessage();
        int i = message.lastIndexOf("UserAuthException: ");
        int exceptionStrLength = "UserAuthException: ".length();
        if (i > -1 && (i + exceptionStrLength) < message.length()) {
            message = message.substring(i + exceptionStrLength).trim();
        }
        return defHandler( message, e, false);
    }

    /**
     * 请求参数解析错误
     * @param e 异常
     * @return -
     */
    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result handleException(MethodArgumentTypeMismatchException e) {
        return defHandler("请求参数错误", e, false);
    }

    private Result defHandler(String msg, Exception e, boolean isError) {
        msg = msg + ":" + IdWorker.getIdStr();
        if (isError) {
            log.error(msg, e);
        } else {
            log.info(msg, e);
        }
        return Result.failed(msg);
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({MissingServletRequestParameterException.class})
    public Result badRequestException(MissingServletRequestParameterException e) {
        return defHandler(e.getMessage(), e, false);
    }

    @ResponseStatus(HttpStatus.OK)
    @ExceptionHandler({ServletException.class})
    public Result badRequestException(ServletException e, HttpServletRequest request) {
        if (request instanceof ContentCachingRequestWrapper) {
            ContentCachingRequestWrapper wrapper = (ContentCachingRequestWrapper) request;
            String requestURI = wrapper.getRequestURI();
            log.error("请求错误，url={}", requestURI, e);
        }
        return defHandler(e.getMessage(), e, false);
    }

    private String getErrMsg(Exception e) {
        String message = e.getLocalizedMessage();
        if(StringUtils.isNotEmpty(message)){
            int i = message.lastIndexOf("UserAuthException: ");
            int exceptionStrLength = "UserAuthException: ".length();
            if (i > -1 && (i + exceptionStrLength) < message.length()) {
                message = message.substring(i + exceptionStrLength).trim();
            } else if (message.startsWith("###")) {
                message = Result.error().getMsg();
            }
        }else {
            message =Result.error().getMsg();
        }
        return message+":" + IdWorker.getIdStr();
    }

}
